package com.fastjars.business.shiro.realms;

import com.fastjars.business.shiro.TokenHolder;
import com.fastjars.business.shiro.exception.*;
import com.fastjars.business.shiro.model.UserModel;
import com.fastjars.business.shiro.model.UserStatus;
import com.fastjars.business.shiro.service.IBaseService;
import com.fastjars.business.shiro.service.IUserService;
import com.fastjars.business.shiro.token.*;
import com.fastjars.business.spring.RequestHolder;
import org.apache.commons.util.PwdUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * AbstractRealm
 *
 * @author Brant Liu <br> 邮箱：<br>lbf1988@qq.com <br>日期：<br>2018/03/22
 * @version 1.0.0
 */
public abstract class AbstractRealm extends AuthorizingRealm {
    private static final String USER_AGENT   = "User-Agent";
    private static final String OR_OPERATOR  = " or ";
    private static final String AND_OPERATOR = " and ";
    private static final String NOT_OPERATOR = "not ";

    @Autowired
    IBaseService baseService;
    @Autowired
    IUserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setRoles(baseService.queryRole(TokenHolder.getUserId()));
        info.setStringPermissions(baseService.queryPermission(TokenHolder.getUserId()));
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        BaseToken token = (BaseToken) authenticationToken;
        UserModel subject = null;
        if(authenticationToken instanceof MobileToken || authenticationToken instanceof MobileSmsCodeToken || token.getRealmType() == BaseToken.TOKEN_LOGIN_BY_MOBILE || token.getRealmType() == BaseToken.TOKEN_LOGIN_BY_MOBILE_CODE){
            subject = userService.queryByMobile(token.getUsername());
            if(UserStatus.FORBIDDEN.name().equalsIgnoreCase(subject.getMobileStatus())){
                throw new MobileForbiddenException();
            }else if(UserStatus.NOT_ACTIVE.name().equalsIgnoreCase(subject.getMobileStatus())){
                throw new MobileNotActiveException();
            }else if(UserStatus.LOCK.name().equalsIgnoreCase(subject.getMobileStatus())){
                throw new MobileLockedAccountException();
            }
        }else if(authenticationToken instanceof UsernameToken || token.getRealmType() == BaseToken.TOKEN_LOGIN_BY_USERNAME){
            subject = userService.queryByUsername(token.getUsername());
        }else if(authenticationToken instanceof EmailToken || token.getRealmType() == BaseToken.TOKEN_LOGIN_BY_EMAIL){
            subject = userService.queryByEmail(token.getUsername());
            if(UserStatus.FORBIDDEN.name().equalsIgnoreCase(subject.getEmailStatus())){
                throw new EmailForbiddenException();
            }else if(UserStatus.NOT_ACTIVE.name().equalsIgnoreCase(subject.getEmailStatus())){
                throw new EmailNotActiveException();
            }else if(UserStatus.LOCK.name().equalsIgnoreCase(subject.getEmailStatus())){
                throw new EmailLockedAccountException();
            }
        }
        if(null == subject){
            //未知用户
            throw new UnknownAccountException();
        }
        validationPrincipal(subject);
        if(null != RequestHolder.getRequest() && null != RequestHolder.getRequest().getHeader(USER_AGENT)) {
            subject.setUserAgent(RequestHolder.getRequest().getHeader(USER_AGENT));
        }
        doGetReturnAfter(token);
        if(authenticationToken instanceof MobileSmsCodeToken){
            return new SimpleAuthenticationInfo(subject, PwdUtils.pwdToHex(subject.getSaltKey(),String.valueOf(token.getPassword()),subject.getSalt()), ByteSource.Util.bytes(subject.getSaltKey()+subject.getSalt()), getName());
        }
        return new SimpleAuthenticationInfo(subject, subject.getPassword(), ByteSource.Util.bytes(subject.getSaltKey()+subject.getSalt()), getName());
    }

    protected abstract void doGetReturnAfter(BaseToken token);

    protected void validationPrincipal(UserModel subject){
        if(UserStatus.LOCK.name().equalsIgnoreCase(subject.getStatus())){
            //账户被锁定
            throw new LockedAccountException();
        }else if(UserStatus.NOT_ACTIVE.name().equalsIgnoreCase(subject.getStatus())){
            //账户未激活
            throw new NotActiveException();
        }else if(UserStatus.FORBIDDEN.name().equalsIgnoreCase(subject.getStatus())){
            //账户禁用
            throw new DisabledAccountException();
        }
    }

    /**
     * 支持or and not 关键词  不支持and or混用
     * @return 真假
     */
    @Override
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        if(permission.contains(OR_OPERATOR)) {
            String[] permissions = permission.split(OR_OPERATOR);
            for(String orPermission : permissions) {
                if(isPermittedWithNotOperator(principals, orPermission)) {
                    return true;
                }
            }
            return false;
        } else if(permission.contains(AND_OPERATOR)) {
            String[] permissions = permission.split(AND_OPERATOR);
            for(String orPermission : permissions) {
                if(!isPermittedWithNotOperator(principals, orPermission)) {
                    return false;
                }
            }
            return true;
        } else {
            return isPermittedWithNotOperator(principals, permission);
        }
    }

    private boolean isPermittedWithNotOperator(PrincipalCollection principals, String permission) {
        if(permission.startsWith(NOT_OPERATOR)) {
            return !super.isPermitted(principals, permission.substring(NOT_OPERATOR.length()));
        } else {
            return super.isPermitted(principals, permission);
        }
    }

    public void clearCachedAuthorizationInfo(){
        PrincipalCollection principalCollection = SecurityUtils.getSubject().getPrincipals();
        SimplePrincipalCollection principals = new SimplePrincipalCollection(principalCollection, getName());
        clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }
}
